#include "WorldState.h"
#include "MenuState.h"
#include "GamePlayState.h"


#include "..\Game.h"
#include "..\Definitions.h"
#include "..\Game Objects\Projectile.h"
#include "..\Game Objects\DestructableObject.h"
#include "..\Game Objects\TriggerObject.h"
#include "..\Game Objects\Event.h"
#include "..\Game Objects\Player.h"
#include "..\Game Objects\Enemy.h"
#include "..\Game Objects\Boss.h"
#include "..\Game Objects\Camera.h"
#include "..\Game Objects\PowerUps.h"
#include "..\Game Objects\CheckPointObject.h"
#include "..\Game Objects\CrumblingPlatformObject.h"
#include "..\Game Objects\WallTriggerObject.h"
#include "..\Game Objects\SwitchTriggerObject.h"
#include "..\Game Objects\SpikeObject.h"
#include "..\Game Objects\SpringboardObject.h"
#include "..\Sprite Management\Animation.h"
#include "..\Tile Management\Layer.h"
#include "..\Tile Management\Tile.h"
#include "..\Tile Management\TileManager.h"

#include "..\Bitmap Font\BitmapFont.h"

#include "..\..\SGD Wrappers\SGD_AudioManager.h"
#include "..\..\SGD Wrappers\SGD_GraphicsManager.h"
#include "..\..\SGD Wrappers\SGD_InputManager.h"

#include "..\..\TinyXML\tinyxml.h"

#include <sstream>


/**************************************************************/
// GetInstance
//	- store the ONLY instance in global memory
//	- return the only instance by pointer
/*static*/ WorldState* WorldState::GetInstance(void)
{
	static WorldState s_Instance;

	return &s_Instance;
}

void WorldState::Enter()
{
	GAME->PlayBGM(false);
	
	// Load From XML
	if (!LoadLevel())
	{
	}

	// Load Pause Image
	hPauseBackground = GRAPHICS->LoadTexture(L"Resource/Images/PauseMenu.png");

	// Set Cursor
	nCursor = 0;

	// Set Game Paused
	bGamePaused = false;

	// Configure Camera
	Camera * camera = Camera::GetInstance();
	camera->SetPosition(SGD::Point(0.0f, 0.0f));
	camera->SetFollowing(true);
	camera->SetSpeed(0.0f);
	camera->SetMaxSpeed(50.0f);

	fVictory = 3.0f;
	bVictory = false;
}

void WorldState::Exit()
{
	GRAPHICS->UnloadTexture(hPauseBackground);

	GAME->PlayBGM(false);
	GAME->GetTileManager()->Clear();

	SGD::InputManager* Input = SGD::InputManager::GetInstance();

	// Unload Objects
	GAME->GetEntityManager().RemoveAll();
}

bool WorldState::Update(float dt)
{
	TileManager* tileManager = GAME->GetTileManager();

	SGD::InputManager* Input = SGD::InputManager::GetInstance();

	if (GAME->GetCheckpoint())
	{
		GAME->GetPlayer()->SetSpawnLoc(GAME->GetCheckpointLoc());
	}

	GAME->GetPlayer()->SetCurrentHP(1000);

	if (GAME->GetEntityManager().GetEntities(1).size() == 0)
	{
		bVictory = true;
	}

	if (bGamePaused)
	{
		if (GamePause())
			return true;
	}
	else if (bVictory)
	{
		fVictory -= dt;

		if (fVictory <= 0)
		{
			GAME->SetCheckpoint(false);
			GAME->SetCurrentLevel(1);
			GAME->ChangeState(GamePlayState::GetInstance());
		}
	}
	else
	{
		if (KEYESCAPE && !bGamePaused)
			bGamePaused = true;

		if (GAME->GetPlayerLives() < 0)
		{
			GAME->SetPlayerLives(3);
			GAME->ChangeState(WorldState::GetInstance());
		}

		// Update and CheckCollisions
		switch (GAME->GetEntityManager().UpdateAll(dt))
		{
		case 1:
		{
				  SetFadeout(GetFadeout() + dt);
				  return true;
		}
		case 2:
		{
				  SetFadeout(0);
				  GAME->SetCheckpoint(false);
				  GAME->ChangeState(GamePlayState::GetInstance());
				  return true;
		}
		}

		// Check Collisions
		tileManager->CheckCollisions(0);
		//  - Player to Enemies
		GAME->GetEntityManager().CheckCollisions(0, 1);
		//  - Player to Boss
		GAME->GetEntityManager().CheckCollisions(0, 2);
		//  - Player to PowerUps
		GAME->GetEntityManager().CheckCollisions(0, 3);
		//  - Player to Events
		GAME->GetEntityManager().CheckCollisions(0, 5);
		//  - Player to Walls
		GAME->GetEntityManager().CheckCollisions(0, 7);
		//  - Player to Switchs
		GAME->GetEntityManager().CheckCollisions(0, 8);
		//  - Player to Crumbling Platforms
		GAME->GetEntityManager().CheckCollisions(0, 9);
		//  - Player to Checkpoint
		GAME->GetEntityManager().CheckCollisions(0, 10);
		//  - Player to Spike
		GAME->GetEntityManager().CheckCollisions(0, 11);
		//  - Player to Springboard
		GAME->GetEntityManager().CheckCollisions(0, 12);
	}
	return true;
}

void WorldState::Render()
{
	TileManager* tileManager = GAME->GetTileManager();

	float xPos = -Camera::GetInstance()->GetPosition().x;
	float xPos2;
	xPos *= 0.5;
	if (xPos < -800)
		xPos2 = xPos + 2408;
	else
		xPos2 = xPos - 2408;

	//unsigned int time = GetTickCount();
	// Render tiles
	tileManager->Render();

	// Render Objects
	GAME->GetEntityManager().RenderAll();

	//time = GetTickCount() - time;

	// Render Fadeout
	if ((GetFadeout() - 3) > 0)
	{
		unsigned char alpha;
		if (((GetFadeout() - 3) * 200) > 255)
			alpha = 255;
		else
			alpha = (unsigned char)((GetFadeout() - 3) * 200);

		GRAPHICS->DrawRectangle(SGD::Rectangle(0.0f, 0.0f, GAME->GetScreenWidth(), GAME->GetScreenWidth()), SGD::Color(alpha, 0, 0, 0));
	}

	// Draw Text Instructions on screen
	// Pause Menu
	SGD::Point pPauseMenuLoc{ 50 + xPos, 50 };
	GAME->GetBitmapFont()->Render("PRESS ESCAPE OR CONTROLLER BUTTON B TO PAUSE", pPauseMenuLoc, 0.4f, SGD::Color{ 255, 50, 50 });
	// Move
	SGD::Point pMovementLoc{ 50 + xPos, 100 };
	GAME->GetBitmapFont()->Render("Use Left/Right Arrows, A and D Keys or", pMovementLoc, 0.4f, SGD::Color{ 210, 210, 210 });
	pMovementLoc.y = 150;
	GAME->GetBitmapFont()->Render("Controller DPad Left/Right to move", pMovementLoc, 0.4f, SGD::Color{ 210, 210, 210 });
	// Jump
	SGD::Point pJumpLoc{ 900 + xPos, 50 };
	GAME->GetBitmapFont()->Render("Press Spacebar or Button X to Jump", pJumpLoc, 0.4f, SGD::Color{ 210, 210, 210 });
	// Wall Jump
	SGD::Point pWallJumpLoc{ 900 + xPos, 100 };
	GAME->GetBitmapFont()->Render("You can wall jump by pushing into the walls then jump", pWallJumpLoc, 0.4f, SGD::Color{ 210, 210, 210 });
	// Spring Jump
	SGD::Point pSpringLoc{ 1300 + xPos, 150 };
	GAME->GetBitmapFont()->Render("Walk into spring board then jump", pSpringLoc, 0.4f, SGD::Color{ 210, 210, 210 });
	// PickUps
	SGD::Point pPickUpsLoc{ 1700 + xPos, 50 };
	GAME->GetBitmapFont()->Render("Pickups can replenish your health", pPickUpsLoc, 0.4f, SGD::Color{ 210, 210, 210 });
	pPickUpsLoc.y = 100;
	GAME->GetBitmapFont()->Render("increase your top speed and", pPickUpsLoc, 0.4f, SGD::Color{ 210, 210, 210 });
	pPickUpsLoc.y = 150;
	GAME->GetBitmapFont()->Render("increase your attack damage.", pPickUpsLoc, 0.4f, SGD::Color{ 210, 210, 210 });
	// Switch/Walls
	SGD::Point pSwitchLoc{ 1700 + xPos, 200 };
	GAME->GetBitmapFont()->Render("Step on buttons to make walls dissappear", pSwitchLoc, 0.4f, SGD::Color{ 255, 50, 50 });
	// CheckPoint
	// Switch/Walls
	SGD::Point pCheckPointLoc{ 2100 + xPos, 50 };
	GAME->GetBitmapFont()->Render("Make sure to go through the checkpoint", pCheckPointLoc, 0.4f, SGD::Color{ 210, 210, 210 });
	pCheckPointLoc.y = 100;
	GAME->GetBitmapFont()->Render("or you will have to restart from beginning", pCheckPointLoc, 0.4f, SGD::Color{ 210, 210, 210 });
	// Crumbling Platforms
	SGD::Point pCrumblingLoc{ 2500 + xPos, 150 };
	GAME->GetBitmapFont()->Render("Watch out for falling ground", pCrumblingLoc, 0.4f, SGD::Color{ 255, 50, 50 });
	// Spikes
	SGD::Point pSpikesLoc{ 2500 + xPos, 200 };
	GAME->GetBitmapFont()->Render("Watch out for spikes, they hurt!", pSpikesLoc, 0.4f, SGD::Color{ 255, 50, 50 });
	// Attacking
	SGD::Point pAttackingLoc{ 2800 + xPos, 50 };
	GAME->GetBitmapFont()->Render("Press Shift key or Button X To Attack", pAttackingLoc, 0.4f, SGD::Color{ 210, 210, 210 });
	// Enemies
	SGD::Point pEnemyLoc{ 3300 + xPos, 50 };
	GAME->GetBitmapFont()->Render("To defeat enemies quickly,", pEnemyLoc, 0.4f, SGD::Color{ 255, 50, 50 });
	pEnemyLoc.y = 100;
	GAME->GetBitmapFont()->Render("run while attacking", pEnemyLoc, 0.4f, SGD::Color{ 255, 50, 50 });


	// Render Pause Menu
	if (bGamePaused)
	{
		// Display Pause Menu
		SGD::Point pCenter{ GAME->GetScreenWidth(), GAME->GetScreenHeight() };

		SGD::Point pBackgroundPauseLoc{ pCenter.x / 2 - 200, pCenter.y / 2 - 150 };
		GRAPHICS->DrawTexture(GAME->hPauseBackgroundImage, pBackgroundPauseLoc, 0.0f, SGD::Vector{}, SGD::Color{ 150, 255, 255, 255 });

		SGD::Color notSelected = { 255, 225, 100 };
		SGD::Color ResumeColor, WorldColor, QuitColor;
		ResumeColor = WorldColor = QuitColor = notSelected;

		switch (nCursor)
		{
		case 0:
			ResumeColor = SGD::Color{ 255, 0, 0 };
			break;
		case 1:
			WorldColor = SGD::Color{ 255, 0, 0 };
			break;
		case 2:
			QuitColor = SGD::Color{ 255, 0, 0 };
			break;
		}

		SGD::Point pPauseLoc{ pCenter.x / 2 - 125, pCenter.y / 2 - 90 };
		GAME->GetBitmapFont()->Render("TUTORIAL PAUSE", pPauseLoc, 0.5f, SGD::Color{ 255, 50, 50 });
		
		SGD::Point pResumeLoc{ pCenter.x / 2 - 112, pCenter.y / 2 - 40 };
		GAME->GetBitmapFont()->Render("Resume Tutorial", pResumeLoc, 0.5f, ResumeColor);
		
		SGD::Point pWorldLoc{ pCenter.x / 2 - 100, pCenter.y / 2 + 10};
		GAME->GetBitmapFont()->Render("Skip Tutorial", pWorldLoc, 0.5f, WorldColor);

		SGD::Point pQuitLoc{ pCenter.x / 2 - 120, pCenter.y / 2 + 60 };
		GAME->GetBitmapFont()->Render("Exit To Main Menu", pQuitLoc, 0.5f, QuitColor);

		bInPauseMenu = true;
	}

	if (bVictory)
	{
		GAME->GetBitmapFont()->Render("You Win!", SGD::Point(305, 305), 1.0f, SGD::Color(0, 70, 0));
		GAME->GetBitmapFont()->Render("You Win!", SGD::Point(300, 300), 1.0f, SGD::Color(60, 255, 0));
	}
}


bool WorldState::LoadLevel()
{
	TileManager* tileManager = GAME->GetTileManager();

	// Load Tutorial Level
	GAME->SetBGM("Resource/Audio/level1bgmusic.xwm");
	tileManager->Load("Resource/Tutorial.xml");
	LoadObjects("Resource/Tutorial.ObjXml");
	GAME->PlayBGM();

	return true;
}


bool WorldState::GamePause()
{
	SGD::InputManager* Input = SGD::InputManager::GetInstance();

	if (bInPauseMenu)
	{
		if (KEYESCAPE)
			nCursor = 2;
	}
	// Move cursor
	if (KEYDOWNPRESSED)
	{
		GAME->SetInput(false);
		++nCursor;

		// Wrap around
		if (nCursor > 2)
			nCursor = 0;
	}
	else if (KEYUPPRESSED)
	{
		GAME->SetInput(false);
		--nCursor;

		// Wrap around
		if (nCursor < 0)
			nCursor = 2;
	}

	// Select an option?
	if (KEYENTER)
	{
		// Which option?
		if (nCursor == 0)
		{
			bGamePaused = false;
			bInPauseMenu = false;
		}

		else if (nCursor == 1)
		{
			// Save Game
			GAME->SetCheckpoint(false);
			GAME->SetCurrentLevel(1);
			GAME->SaveGame();
			GAME->ChangeState(GamePlayState::GetInstance());
			
			// Exit
			return true;
		}

		else if (nCursor == 2)
		{
			// Go To Main Menu
			GAME->SetCheckpoint(false);
			GAME->ChangeState(MenuState::GetInstance());

			// Exit
			return true;
		}
	}

	return false;
}

void WorldState::LoadObjects(const char* fileName)
{
	TiXmlDocument doc;

	if (doc.LoadFile(fileName))
	{
		TileManager* tileManager = GAME->GetTileManager();
		TiXmlElement* pRoot = doc.RootElement();

		if (pRoot != nullptr)
		{
			TiXmlElement* pObject = pRoot->FirstChildElement("Object");

			// Setup WallTriggerObject vector
			std::vector<WallTriggerObject*> wallTriggerObj;

			while (pObject)
			{
				int locX;
				pObject->Attribute("X", &locX);

				int locY;
				pObject->Attribute("Y", &locY);

				SGD::Point loc = SGD::Point{ (float)locX * tileManager->GetTileSize().width, (float)locY * tileManager->GetTileSize().height };

				std::string type;
				type = pObject->Attribute("Type");

				if (type == "PLAYER_OBJ")
				{
					GAME->CreatePlayer(loc);
				}
				else if (type == "MELEE_GRUNT_OBJ")
				{
					GAME->CreateEnemy(loc, GameObject::ObjectID::MELEE);
				}
				else if (type == "MAGE_GRUNT_OBJ")
				{

				}
				else if (type == "RANGED_GRUNT_OBJ")
				{
					GAME->CreateEnemy(loc, GameObject::ObjectID::RANGED);
				}
				else if (type == "EVENT")
				{
					GAME->CreateEventt(loc);
				}
				else if (type == "CHECK_POINT_OBJ")
				{
					GAME->CreateCheckPoint(loc, GameObject::ObjectID::CHECK_POINT);

					// Check if player is at check point - if yes, change position to checkpoint
					if (GAME->GetCheckpoint())
						GAME->GetPlayer()->SetPosition(loc);
				}
				else if (type == "HEALTH_POWERUP_OBJ")
				{
					GAME->CreateHPPowerUp(loc, GameObject::ObjectID::POWERUP_HEALTH);
				}
				else if (type == "SPEED_POWERUP_OBJ")
				{
					GAME->CreateSpeedPowerUp(loc, GameObject::ObjectID::POWERUP_SPEED);
				}
				else if (type == "DAMAGE_POWERUP_OBJ")
				{
					GAME->CreateDamagePowerUp(loc, GameObject::ObjectID::POWERUP_DAMAGE);
				}
				else if (type == "CRUMBLING_PLATFORM_OBJ")
				{
					GAME->CreateCrumblingPlatform(loc, GameObject::ObjectID::CRUMBLING_PLATFORM);
				}
				else if (type == "WALL_TRIGGER_OBJ")
				{
					// Check for owner's ID
					int ownerID;
					pObject->Attribute("OwnerID", &ownerID);

					WallTriggerObject* trigger = GAME->CreateWallTriggerObject(loc, GameObject::ObjectID::WALL_TRIGGER_OBJECT, (unsigned int)ownerID);

					// Add wall trigger objects to vector for switch trigger access
					wallTriggerObj.push_back(trigger);
				}
				else if (type == "SWITCH_TRIGGER_OBJ")
				{
					// Check for target's ID
					int targetID;
					pObject->Attribute("TargetID", &targetID);

					GAME->CreateSwitchTrigger(loc, GameObject::ObjectID::SWITCH_TRIGGER, (unsigned int)targetID);
				}
				else if (type == "SPIKE_OBJ")
				{
					GAME->CreateSpike(loc, GameObject::ObjectID::SPIKE);
				}
				else if (type == "SPRINGBOARD_OBJ")
				{
					GAME->CreateSpringboard(loc, GameObject::ObjectID::SPRINGBOARD);
				}
				else if (type == "BOSS_OBJ")
				{
					GAME->CreateBoss(loc, GameObject::ObjectID::BOSS, 1);
				}

				pObject = pObject->NextSiblingElement("Object");
			}
			// Store wall trigger objects inside of Game
			GAME->SetWallTriggerObjectsVector(wallTriggerObj);
		}
	}
}


void WorldState::SetPlayerPowerUps(const int dam, const int hp, const float speed)
{
	GAME->GetPlayer()->SetDamage(GAME->GetPlayer()->GetDamage() + dam);
	GAME->GetPlayer()->SetCurrentHP(GAME->GetPlayer()->GetCurrentHP() + hp);
	GAME->GetPlayer()->SetMaxSpeed(GAME->GetPlayer()->GetMaxSpeed() + speed);
}


float WorldState::GetFadeout()
{
	return fFadeout;
}

void WorldState::SetFadeout(float time)
{
	fFadeout = time;
}

void WorldState::SetVictory(bool win)
{
	bVictory = win;
}